﻿//Copyright 2012 - Jonathan Allen

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Diagnostics.CodeAnalysis;

namespace Granite.Xaml.Internals
{
	/// <summary>
	/// This navigator holds its own journal. You can use it as a mock for testing or as a base-class for a custom navigation scheme.
	/// </summary>
	public class SimpleNavigator : INavigator
	{
		readonly Stack<Uri> m_BackStack = new Stack<Uri>();
		readonly Stack<Uri> m_ForwardStack = new Stack<Uri>();
		Uri m_Current;

		/// <summary>
		/// Navigate to the indicated URI
		/// </summary>
		/// <param name="target"></param>
		public virtual void Navigate(Uri target)
		{
			if (m_Current != null)
				m_BackStack.Push(m_Current);
			m_Current = target;
			m_ForwardStack.Clear();

			if (Navigated != null)
				Navigated(this, new UriEventArgs(target));
		}

		/// <summary>
		/// This event is raised when navigation occurs via Navigate, GoForward, or GoBack
		/// </summary>
		/// <remarks>
		/// If IncludeWebInNavigation is set to True, GoForward and GoBack may raise this event for website URIs.
		/// </remarks>
		public event EventHandler<UriEventArgs> Navigated;

		/// <summary>
		/// This event is raised when navigation occurs via OpenWebsite
		/// </summary>
		public event EventHandler<UriEventArgs> WebsiteOpened;

		/// <summary>
		/// Opens a website.
		/// </summary>
		/// <param name="target"></param>
		/// <remarks>This does not affect the back stack unless IncludeWebInNavigation is set to true.</remarks>
		public virtual void OpenWebsite(Uri target)
		{
			if (IncludeWebInNavigation)
			{
				if (m_Current != null)
					m_BackStack.Push(m_Current);
				m_Current = target;
				m_ForwardStack.Clear();
			}
			if (WebsiteOpened != null)
				WebsiteOpened(this, new UriEventArgs(target));
		}

		/// <summary>
		/// Indicates whether or not websites are included in the journal
		/// </summary>
		public bool IncludeWebInNavigation { get; set; }

		/// <summary>
		/// Indicates that GoBack can be safely called.
		/// </summary>
		public virtual bool CanGoBack
		{
			get
			{
				Contract.Ensures(Contract.Result<bool>() == false || m_BackStack.Count > 0);
				return m_BackStack.Count > 0;
			}

		}

		/// <summary>
		/// Indicates that GoForward can be safely called.
		/// </summary>
		public virtual bool CanGoForward
		{
			get
			{
				Contract.Ensures(Contract.Result<bool>() == false || m_ForwardStack.Count > 0);
				return m_ForwardStack.Count > 0;
			}
		}

		/// <summary>
		/// Return to the previous View in the navigation journal.
		/// </summary>
		[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "CanGoBack")]
		public virtual void GoBack()
		{
			if (!CanGoBack)
				throw new InvalidOperationException("CanGoBack is false");

			if (m_Current != null)
				m_ForwardStack.Push(m_Current);

			Contract.Assume(m_BackStack.Count > 0);

			m_Current = m_BackStack.Pop();

			if (Navigated != null)
				Navigated(this, new UriEventArgs(m_Current));
		}

		/// <summary>
		/// Go to the next view in the navigation journal.
		/// </summary>
		[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "CanGoForward")]
		public virtual void GoForward()
		{
			if (!CanGoForward)
				throw new InvalidOperationException("CanGoForward is false");

			if (m_Current != null)
				m_BackStack.Push(m_Current);

			Contract.Assume(m_ForwardStack.Count > 0);

			m_Current = m_ForwardStack.Pop();

			if (Navigated != null)
				Navigated(this, new UriEventArgs(m_Current));
		}


		[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
		[ContractInvariantMethod]
		void ContractInvariant()
		{
			Contract.Invariant(m_BackStack != null);
			Contract.Invariant(m_ForwardStack != null);
		}
	}

}
